import { isObjectLike, isArray, cloneDeep, filter, merge, uniq, uniqBy, find } from 'lodash'
import { mul, subtraction } from '@/utils'
import commonIcon from '../assets/icon'
import { initPublicConfig } from '../assets/public'
import typeConfig from './typeConfig'

// 对serles中的type类型进行排序
function sortSeries(d) {
  const o = {}
  let r = []
  if (d && d.length > 0) {
    d.forEach((item) => {
      r.push(item.type)
    })
    r = Array.from(new Set(r))
    r.forEach((item) => {
      o[item] = filter(d, (v) => v.type === item)
    })
  }
  return o
}

// 处理图表数据data
function formatData(data, type) {
  const specialType = ['candlestick', 'k', 'boxplot']
  let result = []
  if (data) {
    data.forEach((item) => {
      if (isObjectLike(item)) {
        if (isArray(item)) {
          let target = item
          if (target.length >= 2 && (specialType.indexOf(type) < 0 || type === 'custom')) {
            //k线图 || 自定义除外
            target = item[1]
          }
          result = result.concat(target)
        } else if (!isArray(item)) {
          if (item.value) {
            if (isObjectLike(item.value)) {
              if (isArray(item.value)) {
                let valueTarget = item.value
                // k线图除外
                if (
                  valueTarget.length >= 2 &&
                  (specialType.indexOf(type) < 0 || type === 'custom')
                ) {
                  valueTarget = valueTarget[1]
                }
                result = result.concat(valueTarget)
              }
            } else {
              result.push(item.value)
            }
          }
        }
      } else {
        result.push(item)
      }
    })
  }
  return result
}

// 处理min是max是function时的转换
function formatMaxMin(type, item, obj, config) {
  let o = item
  const specialString = {
    min: 'dataMin',
    max: 'dataMax'
  }
  if (o || o === 0) {
    if (typeof o === 'function') {
      o = item(obj)
    } else if (typeof o === 'string') {
      if ((o = specialString[type])) {
        delete config[type]
        o = obj[type]
      }
    }
  } else {
    o = obj[type]
  }
  return Number(o)
}

// 计算堆叠数据
function stackData(data) {
  let result = data.concat()
  let newData = []
  result = result.map((a, i) => {
    const thisItem = formatData(a.data, a.type)
    if (i === 0) {
      newData = thisItem
    } else {
      newData = newData.map((num, index) => {
        const newNum = thisItem[index] || 0
        const basicNum = num || 0
        return Number(basicNum) + Number(newNum)
      })
    }
    return {
      ...a,
      data: newData
    }
  })
  result = result.concat(data)
  return result
}

function mixData(data) {
  let targetData = data.concat()
  let result = []
  // 是否需要堆叠数据
  const isStack = filter(targetData, (v) => v.stack).length > 0
  if (isStack) {
    let stackArr = []
    targetData.forEach((item) => {
      stackArr.push(item.stack)
      stackArr = filter(stackArr, (v) => v)
      stackArr = uniq(stackArr)
    })
    const needStackData = []
    stackArr.forEach((item) => {
      const thisStackArr = filter(data, (v) => v.stack === item)
      needStackData.push(thisStackArr)
    })
    needStackData.forEach((item) => {
      targetData = targetData.concat(stackData(item))
    })
  }
  targetData.forEach((item) => {
    const thisItemData = formatData(item.data, item.type)
    result = result.concat(thisItemData)
  })
  return result
}

function calculateyAxisMaxMin(config, series, index) {
  if (config.type === 'value') {
    // 找出相同yAxisIndex的数据
    const sameIndexSeries = filter(series, (v) => {
      const thisIndex = v.yAxisIndex ? Number(v.yAxisIndex) : null
      return thisIndex === index
    })
    let data = []
    if (sameIndexSeries.length < 1) {
      const noyAxisIndexSeries = filter(series, (v) => !v.yAxisIndex)
      data = mixData(noyAxisIndexSeries)
    } else {
      data = mixData(sameIndexSeries)
    }
    data = data.filter((v) => (v && typeof Number(v) === 'number' && !isNaN(Number(v))) || v === 0) // 过滤没有值的数据
    data = data.sort((a, b) => a - b)
    let min = data[0] || 0
    let max = data[data.length - 1] || 1
    // min = mul(min, min > 0 ? 0.8 : 1.2)
    // max = mul(max, max > 0 ? 1.2 : 0.8)
    const MNobj = {
      min,
      max
    }
    min = formatMaxMin('min', config.min, MNobj, config)
    max = formatMaxMin('max', config.max, MNobj, config)

    // 最大最小值相同时
    if (max === min) {
      if (max > 0) {
        min = 0
      } else if (max < 0) {
        max = 0
      } else {
        max = 1
      }
    }

    const { splitNumber = 4 } = config
    let interval = subtraction(max, min) / splitNumber

    // 分值最小为0.01
    if (Math.abs(interval) < 0.01) {
      if (interval < 0.01 && interval > 0) {
        interval = 0.01
      }
      if (interval > -0.01 && interval < 0) {
        interval = -0.01
      }
      max = min + interval * splitNumber
    }

    // 取整
    if (config.round) {
      interval = interval > 0 ? Math.ceil(interval) : Math.floor(interval)
      min = Math.floor(min)
      max = min + interval * splitNumber
    }

    return merge(
      {},
      {
        max,
        min,
        interval
      },
      config
    )
  }
  return config
}

// 获取字符串长度
function getStrCharLen(str) {
  let len = 0
  for (const i in str) {
    const t = str.chartCodeAt(i)
    if ((t >= 0x001 && t <= 0x007e) || (t >= 0xff60 && t <= 0xff9f)) {
      len++
    } else {
      len += 2
    }
  }
  len /= 2
  return len
}
// y轴那么配置
function formatyAxisName(config, index) {
  const result = config
  let distance = 0
  if (config && config.name) {
    const l = getStrCharLen(config.name)
    distance = l > 1 ? (l / 2 - 1) * 24 : 0
  }
  let padding = [0, 0, 0, distance]
  if (index === 1) {
    padding = [0, distance, 0, 0]
  }
  return merge({}, result, {
    nameTextStyle: {
      padding
    }
  })
}
//设置图例图标
function setLegendIcon(config = {}, series) {
  const data = []
  let names = []
  let legendData = config.data || []
  if (legendData.length > 0) {
    legendData = legendData.map((item) => {
      const newItem = isObjectLike(item) ? item : { name: item }
      return newItem
    })
  }
  const targetData = legendData.length > 0 ? legendData : series
  targetData.forEach((item) => {
    const { name } = item
    if (name && name !== '') {
      names.push(name)
    }
  })
  names = uniq(names)
  names = uniq(names)

  const { line, rect, dot } = commonIcon

  names.forEach((item) => {
    const customItem = find(legendData, (v) => v.name === item) || {}
    let icon = customItem.icon || line
    let t = filter(targetData, (v) => v.name === item)
    t = uniqBy(t, 'type')
    if (t.length > 0) {
      if (t.length === 1) {
        const { type } = t[0]
        switch (type) {
          case 'line':
            icon = line
            break
          case 'k':
            icon = line
            break
          case 'candlestick':
            icon = line
            break
          case 'bar':
            icon = line
            break
          case 'scatter':
            icon = dot
            break
          case 'bubble':
            icon = dot
            break
          default:
            break
        }
      }
    }
    data.push({
      name: item,
      icon
    })
  })
  return merge({}, { data }, config)
}

function singleRender(props) {
  const {
    target,
    config,
    key,
    originOption: { series },
    index
  } = props
  let r = target.notMerge ? merge({}, target) : merge({}, config, target)
  const formatValue = (v) => (/[+-]?d*\.\d*$/.test(v) ? Number(v.toFixed(2)) : v)
  if (key === 'yAxis' && !r.autoMaxMin) {
    r = calculateyAxisMaxMin(r, series, index) // 计算最大最小值
    r = formatyAxisName(r, index) // 根据name长度设置nam额的位置
    if (r.axisLabel & r.axisLabel.formatter) {
      const { formatter } = r.axisLabel
      if (typeof formatter === 'function') {
        r.axisLabel.formatter = (v) => formatter(formatValue(v))
      } else {
        const w = String(r.axisLabel.formatter)
        r.axisLabel.formatter = (v) => w.replace(/^{value}/, formatValue(v))
      }
    } else {
      r.axisLabel.formatter = (v) => formatValue(v)
    }

    if (r.round) {
      r.axisPointer = {
        label: {
          formatter: (params) => {
            const { value } = params
            return /^[+-]?\d*\.\d*$/.test(value) ? Number(Math.round(value)) : value
          }
        }
      }
    }
  }
  if (key === 'legend') {
    r = setLegendIcon(r, series)
  }
  return r
}

function commonRender(props) {
  const { config, target } = props
  let t = cloneDeep(target)
  if (isObjectLike(t)) {
    if (isArray(t)) {
      t = t.map((item, index) => {
        const o = singleRender({
          ...props,
          target: item,
          index
        })
        return o
      })
    } else {
      t = singleRender({
        ...props,
        target: t,
        index: 0
      })
    }
  } else if (!isObjectLike(t)) {
    if (t) {
      t = singleRender({
        ...props,
        target: t,
        index: 0
      })
    } else {
      t = config
    }
  }
  return t
}

function seriesRender(props) {
  const {
    target,
    chartTheme,
    originOption: { color }
  } = props
  const result = []
  const newTypeConfig = typeConfig(chartTheme, color && color.length > 0 ? color : null)
  const seriesCombine = sortSeries(cloneDeep(target))
  const keys = Object.keys(seriesCombine)
  keys.forEach((item) => {
    const children = seriesCombine[item]
    const len = children.length
    const t = newTypeConfig[item]
    children.forEach((child, index) => {
      const temp = merge({}, t.renderOption({ type: item, length: len, custom: child, index }), {
        ...child,
        type: t.type
      })
      result.push(t ? temp : child)
    })
  })
  return result
}

function checkIsOnlyType(data) {
  const seriesCombine = sortSeries(cloneDeep(data))
  const keys = Object.keys(seriesCombine)
  let result = {
    isOnly: false,
    type: keys
  }
  if (keys.length > 0) {
    const target = keys[0]
    const isOnly = filter(keys, (v) => v !== target).length < 1
    result = {
      isOnly,
      type: isOnly ? target : keys
    }
  }
  return result
}

function setRules({ data, theme, chartTheme }) {
  const { axisLabelColor, axisLineColor, axisSplitLine } = chartTheme
  const { type } = checkIsOnlyType(data.series)
  const publicConfig = initPublicConfig(theme)
  const specialType = ['scatter', 'bubble'] // 散点图 && 气泡图
  const basicGutter = 6
  let gridTop = 35
  let gridBottom = 12
  let dataZoomHeight = 18
  let dataZoomBottom = 0
  const legendHeight = 14
  let legendBottom = 0

  if (data.legend && data.legend.show !== false) {
    legendBottom = gridBottom
    gridBottom += legendHeight + basicGutter
  }
  if (data.dataZoom) {
    const checkDataZoom = (p) => {
      if (!p.type || p.type === 'slider') {
        if (p.size) {
          if (p.size === 'sm') {
            dataZoomHeight = 12
          } else if (p.size === 'lg') {
            dataZoomHeight = 24
          } else {
            const customSize = Number(p.size)
            if (typeof customSize === 'number') {
              dataZoomHeight = customSize
            }
          }
        }
        dataZoomBottom = gridBottom
        gridBottom += dataZoomHeight + basicGutter * 2
      } else {
        dataZoomHeight = 'auto'
        dataZoomBottom = 'auto'
      }
    }
    if (isObjectLike(data.dataZoom)) {
      if (isArray(data.dataZoom)) {
        const filterDataZoom = filter(data.dataZoom, (v) => !v.type || v.type === 'slider')
        if (filterDataZoom.length > 0) {
          checkDataZoom(filterDataZoom[0])
        }
      } else {
        checkDataZoom(data.dataZoom)
      }
    } else {
      checkDataZoom(data.dataZoom)
    }
  }
  if (data.title) {
    gridTop += 25
  }
  return {
    grid: {
      normal: {
        containLabel: true,
        top: gridTop,
        left: 12,
        right: 12,
        bottom: gridBottom
      },
      render: commonRender
    },
    tooltip: {
      normal: {
        ...publicConfig.tooltip
      },
      render: commonRender
    },
    legend: {
      normal: {
        ...publicConfig.legend,
        height: legendHeight,
        bottom: legendBottom
      },
      render: commonRender
    },
    dataZoom: {
      normal: {
        type: 'slider',
        height: dataZoomHeight,
        left: 100,
        right: 100,
        bottom: dataZoomBottom,
        textStyle: {
          color: theme.normalFontColor
        }
      },
      render: commonRender
    },
    title: {
      normal: {
        show: true,
        top: 'top',
        left: 'center',
        textStyle: {
          color: theme.normalFontColor,
          fontSize: 14,
          fontWeight: 400
        }
      },
      render: commonRender
    },
    xAxis: {
      normal: {
        type: specialType.indexOf(type) > -1 ? 'value' : 'category',
        nameTextStyle: {
          color: axisLabelColor
        },
        axisTick: {
          show: false
        },
        axisLine: {
          lineStyle: {
            color: axisLineColor
          }
        },
        axisLabel: {
          color: axisLabelColor
        },
        splitLine: {
          show: specialType.indexOf(type) > -1,
          lineStyle: {
            type: 'dashed',
            color: axisSplitLine
          }
        }
      },
      render: commonRender
    },
    yAxis: {
      normal: {
        type: 'value',
        nameTextStyle: {
          color: axisLabelColor
        },
        axisTick: {
          show: false
        },
        axisLine: {
          lineStyle: {
            color: axisLineColor
          }
        },
        axisLabel: {
          color: axisLabelColor
        },
        splitLine: {
          show: true,
          lineStyle: {
            type: 'dashed',
            color: axisSplitLine
          }
        }
      },
      render: commonRender
    },
    series: {
      render: seriesRender
    }
  }
}

function pullNewOptions({ option = {}, theme, chartTheme }) {
  const originOption = cloneDeep(option)
  const normalConfig = {
    color: chartTheme.seriesColor,
    tooltip: true,
    legend: true
  }
  if (isObjectLike(originOption.series)) {
    if (!isArray(originOption.series)) {
      originOption.series = [originOption.series]
    }
  } else {
    originOption.series = []
  }
  const newOption = merge({}, normalConfig, originOption)
  const result = {}
  const rules = setRules({ data: newOption, theme, chartTheme })
  const keys = Object.keys(option)
  keys.forEach((key) => {
    const target = rules[key]
    if (newOption[key]) {
      result[key] = target
        /*eslint-disable*/
        ? target.render({
          config: target.normal, // 默认参数
          target: newOption[key], // 传入参数
          chartTheme, //参数配置
          theme,
          key,
          originOption: newOption // 全部参数
        })
        : newOption[key]
    }
  })
  return result
}

export default pullNewOptions
